﻿/****************************************************
	文件：RuntimeUtil.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2021/07/03 12:55:05
	功能：运行时的帮助方法
*****************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using UnityEngine;
using UnityEngine.UI;

namespace HTFramework.Utility
{
    public class RuntimeUtil
    {
        /// <summary>
        /// 检测指定代码段消耗
        /// </summary>
        /// <param name="funcName">自定义名称</param>
        /// <param name="func">待检测的指定代码段</param>
        public static void ProfilerSample(string funcName, Action func)
        {
            if (func == null) return;
            UnityEngine.Profiling.Profiler.BeginSample(funcName);
            func.Invoke();
            UnityEngine.Profiling.Profiler.EndSample();
        }


        //------------------------------  UI 相关  ------------------------------//

        #region UI 相关

        /// <summary>
        /// 递归设置层级
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="layerMask"></param>
        public static void SetLayerRecursively(GameObject gameObject, LayerMask layerMask)
        {
            gameObject.layer = layerMask;
            for (int i = 0; i < gameObject.transform.childCount; i++)
            {
                SetLayerRecursively(gameObject.transform.GetChild(i).gameObject, layerMask);
            }
        }

        /// <summary>
        /// 递归设置层级
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="layerName"></param>
        public static void SetLayerRecursively(GameObject gameObject, string layerName)
        {
            LayerMask layerMask = LayerMask.NameToLayer(layerName);
            SetLayerRecursively(gameObject, layerMask);
        }

        /// <summary>
        /// 设置 sortingOrder（Canvas 和 Renderer 的 sortingOrder）
        /// </summary>
        /// <param name="root"></param>
        /// <param name="sortingOrder"></param>
        public static void SetSortingOrder(GameObject root, int sortingOrder)
        {
            Canvas[] canvasArray = root.GetComponentsInChildren<Canvas>(true);
            if (canvasArray != null && canvasArray.Length > 0)
            {
                foreach (var canvas in canvasArray)
                    canvas.sortingOrder = sortingOrder;
            }

            Renderer[] renderArray = root.GetComponentsInChildren<Renderer>(true);
            if (renderArray != null && renderArray.Length > 0)
            {
                foreach (var render in renderArray)
                    render.sortingOrder = sortingOrder;
            }
        }

        /// <summary>
        /// 增加 sortingOrder（Canvas 和 Renderer 的 sortingOrder）
        /// </summary>
        /// <param name="root"></param>
        /// <param name="addOrder"></param>
        public static void AddSortingOrder(GameObject root, int addOrder)
        {
            if (addOrder == 0) return;

            Canvas[] canvasArray = root.GetComponentsInChildren<Canvas>(true);
            if (canvasArray != null && canvasArray.Length > 0)
            {
                foreach (var canvas in canvasArray)
                    canvas.sortingOrder += addOrder;
            }

            Renderer[] renderArray = root.GetComponentsInChildren<Renderer>(true);
            if (renderArray != null && renderArray.Length > 0)
            {
                foreach (var render in renderArray)
                    render.sortingOrder += addOrder;
            }
        }

        /// <summary>
        /// 设置 UI 为灰色，且不可交互
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="isGray"></param>
        public static void SetGrayEnabled(GameObject gameObject, bool isGray)
        {
            Image img;
            img = gameObject.GetComponent<Image>();

            if (img != null)
            {
                if (isGray)
                {
                    Material material = new Material(Shader.Find("Custom/Gray"));
                    img.material = material;
                }
                else
                    img.material = null;

                //按键不可触摸
                img.raycastTarget = false;
            }

            foreach (Transform ts in gameObject.transform)
            {
                SetGrayEnabled(ts.gameObject, isGray);
            }
        }

        #endregion


        //------------------------------  反射相关  ------------------------------//

        #region 反射相关

        /// <summary>
        ///  通过反射获取对象内部变量/属性值
        /// </summary>
        /// <param name="obj">待反射对象</param>
        /// <param name="memberName">变量名</param>
        /// <param name="bindingFlags">反射参数</param>
        /// <returns></returns>
        public static object GetMemberValue(object obj, string memberName,
            BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)
        {
            object value = null;
            Type type = obj.GetType();
            MemberInfo[] memberInfos = type.GetMember(memberName, bindingFlags);
            foreach (MemberInfo memberInfo in memberInfos)
            {
                switch (memberInfo.MemberType)
                {
                    case MemberTypes.Field:
                        value = type.GetField(memberName, bindingFlags).GetValue(obj);
                        break;
                    case MemberTypes.Property:
                        value = type.GetProperty(memberName, bindingFlags).GetValue(obj);
                        break;
                }
            }

            return value;
        }

        /// <summary>
        /// 通过反射设置对象内部变量/属性值
        /// </summary>
        /// <param name="obj">待反射对象</param>
        /// <param name="memberName">变量名</param>
        /// <param name="value">待设置的值</param>
        /// <param name="bindingFlags">反射参数</param>
        public static void SetMemberValue(object obj, string memberName, object value,
            BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)
        {
            Type type = obj.GetType();
            MemberInfo[] memberInfos = type.GetMember(memberName, bindingFlags);
            foreach (MemberInfo memberInfo in memberInfos)
            {
                switch (memberInfo.MemberType)
                {
                    case MemberTypes.Field:
                        FieldInfo fieldInfo = type.GetField(memberName);
                        fieldInfo.SetValue(obj, value);
                        break;
                    case MemberTypes.Property:
                        PropertyInfo propertyInfo = type.GetProperty(memberName);
                        propertyInfo.SetValue(obj, value);
                        break;
                }
            }
        }

        /// <summary>
        /// 通过反射设置对象内部变量/属性值
        /// </summary>
        /// <param name="obj">待反射对象</param>
        /// <param name="memberName">变量名</param>
        /// <param name="valueStr">字符串形式的值</param>
        /// <param name="bindingFlags">反射参数</param>
        public static void SetMemberValue(object obj, string memberName, string valueStr,
            BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)
        {
            object value = null;
            Type type = obj.GetType();
            MemberInfo[] memberInfos = type.GetMember(memberName, bindingFlags);
            foreach (MemberInfo memberInfo in memberInfos)
            {
                switch (memberInfo.MemberType)
                {
                    case MemberTypes.Field:
                        FieldInfo fieldInfo = type.GetField(memberName);
                        value = TypeDescriptor.GetConverter(fieldInfo.FieldType).ConvertFromInvariantString(valueStr);
                        fieldInfo.SetValue(obj, value);
                        break;
                    case MemberTypes.Property:
                        PropertyInfo propertyInfo = type.GetProperty(memberName);
                        value = TypeDescriptor.GetConverter(propertyInfo.PropertyType)
                            .ConvertFromInvariantString(valueStr);
                        propertyInfo.SetValue(obj, value);
                        break;
                }
            }
        }

        /// <summary>
        /// 反射调用方法
        /// </summary>
        /// <param name="obj">待反射对象</param>
        /// <param name="method">方法名</param>
        /// <param name="bindingFlags">反射参数</param>
        /// <param name="paramArray">方法参数列表</param>
        /// <returns></returns>
        public static object InvokeMethod(object obj, string method,
            BindingFlags bindingFlags = BindingFlags.Default | BindingFlags.InvokeMethod, params object[] paramArray)
        {
            return obj.GetType().InvokeMember(method, bindingFlags, null, obj, paramArray);
        }

        /// <summary>
        /// 获取参数个数最多的构造函数
        /// </summary>
        /// <param name="ctorInfos">所有的构造函数</param>
        /// <returns></returns>
        public static ConstructorInfo GetCtor_ParameterMax(ConstructorInfo[] ctorInfos)
        {
            ConstructorInfo ctor = ctorInfos[0];
            int paramCnt = ctor.GetParameters().Length;
            for (int i = 1; i < ctorInfos.Length; i++)
            {
                int length = ctorInfos[i].GetParameters().Length;
                if (length >= paramCnt)
                {
                    paramCnt = length;
                    ctor = ctorInfos[i];
                }
            }

            return ctor;
        }

        /// <summary>
        /// 尝试通过自定义特性获取构造函数
        /// </summary>
        /// <param name="ctorInfos">构造函数数组</param>
        /// <param name="AttributeType">自定义属性</param>
        /// <param name="ctorInfo">构造函数</param>
        /// <returns></returns>
        public static bool TryGetCtorByAttribute(ConstructorInfo[] ctorInfos, Type AttributeType,
            out ConstructorInfo ctorInfo)
        {
            for (int i = 0; i < ctorInfos.Length; i++)
            {
                if (ctorInfos[i].IsDefined(AttributeType))
                {
                    ctorInfo = ctorInfos[i];
                    return true;
                }
            }

            ctorInfo = null;
            return false;
        }

        /// <summary>
        /// 反射创建类实例（默认使用构造函数中参数最多的构造函数进行对象创建，实现递归，若互为参数可能陷入递归死循环）
        /// </summary>
        /// <param name="type">类型</param>
        /// <returns></returns>
        public static object CreateClassObjRecursion(Type type)
        {
            if (type.IsArray)
                return null;
            if (!type.IsClass)
                return default;
            if (type == typeof(string))
                return string.Empty;

            ConstructorInfo[] ctorInfos = type.GetConstructors();

            //--- 默认使用构造函数中参数最多的构造函数
            ConstructorInfo ctorInfo = GetCtor_ParameterMax(ctorInfos);
            ParameterInfo[] parameterInfos = ctorInfo.GetParameters();

            //--- 无参构造函数
            if (parameterInfos.Length == 0)
                return Activator.CreateInstance(type);

            //--- 有参构造函数
            object[] paramArray = new object[parameterInfos.Length];
            for (int i = 0; i < parameterInfos.Length; i++)
            {
                Type paramType = parameterInfos[i].ParameterType;
                object paramObj = CreateClassObjRecursion(paramType); //递归调用
                paramArray[i] = paramObj;
            }

            return Activator.CreateInstance(type, paramArray);
        }

        /// <summary>
        /// 反射创建类实例（使用无参构造函数）
        /// </summary>
        /// <param name="fullClassName">类名（包含命名空间）</param>
        /// <returns></returns>
        public static object CreateClassObj(string fullClassName)
        {
            object obj = null;
            Type type = null;
            foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = asm.GetType(fullClassName);
                if (type != null)
                {
                    obj = Activator.CreateInstance(type);
                    break;
                }
            }

            return obj;
        }

        /// <summary>
        /// 反射创建类实例（使用无参构造函数）
        /// </summary>
        /// <typeparam name="T">类的类型</typeparam>
        /// <returns></returns>
        public static T CreateClassObj<T>() where T : class, new()
        {
            Type t = typeof(T);
            ConstructorInfo constructor = t.GetConstructor(Type.EmptyTypes);
            return (T) constructor.Invoke(null); //调用无参的构造函数
        }

        /// <summary>
        /// 反射创建List
        /// </summary>
        /// <param name="itemType">List内元素的类型</param>
        /// <returns></returns>
        public static object CreateList(Type itemType)
        {
            Type lstType = typeof(List<>);
            Type specType = lstType.MakeGenericType(new Type[] {itemType}); //确定 泛型 类型
            object lst = Activator.CreateInstance(specType, new object[] { }); //new 出 List
            return lst;
        }

        //基本思想是：一个对象所占据的内存空间，取决于它的实例字段（包括继承树上的私有实例字段）
        public static T DeepCloneObject<T>(T obj) where T : class
        {
            //System.String类型似乎比较特殊，复制它的所有字段，并不能复制它本身
            //不过由于System.String的不可变性，即使指向同一对象，也无所谓
            //而且.NET里本来就用字符串池来维持
            if (obj == null || obj.GetType() == typeof(string))
                return obj;
            object newObj = null;
            try
            {
                //尝试调用默认构造函数
                newObj = Activator.CreateInstance(obj.GetType());
            }
            catch
            {
                //失败的话，只好枚举构造函数了
                foreach (ConstructorInfo ci in obj.GetType()
                    .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    try
                    {
                        ParameterInfo[] pis = ci.GetParameters();
                        object[] objs = new object[pis.Length];
                        for (int i = 0; i < pis.Length; i++)
                        {
                            if (pis[i].ParameterType.IsValueType)
                                objs[i] = Activator.CreateInstance(pis[i].ParameterType);
                            else
                                //参数类型可能是抽象类或接口，难以实例化
                                //我能想到的就是枚举应用程序域里的程序集，找到实现了该抽象类或接口的类
                                //但显然过于复杂了
                                objs[i] = null;
                        }

                        newObj = ci.Invoke(objs);
                        //无论调用哪个构造函数，只要成功就行了
                        break;
                    }
                    catch
                    {
                    }
                }
            }

            foreach (FieldInfo fi in obj.GetType()
                .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
                    fi.SetValue(newObj, fi.GetValue(obj));
                else
                    fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
            }

            //基类的私有实例字段在子类里检索不到，但它仍占据子类对象的内存空间
            Deep(newObj, obj);
            return (T) newObj;
        }

        //克隆继承树上的私有实例字段
        public static void Deep(object newObj, object obj)
        {
            for (Type father = newObj.GetType().BaseType; father != typeof(object); father = father.BaseType)
            {
                foreach (FieldInfo fi in father.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    //只需要处理私有字段，因为非私有成员已经在子类处理过了
                    if (fi.IsPrivate)
                    {
                        if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
                        {
                            fi.SetValue(newObj, fi.GetValue(obj));
                        }
                        else
                        {
                            fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
                        }
                    }
                }
            }
        }

        #endregion


        //------------------------------  序列化与反序列化相关  ------------------------------//

        #region 序列化与反序列化相关

        /// <summary>
        /// 对象 转 Xml
        /// </summary>
        /// <param name="path">Xml存储地址</param>
        /// <param name="obj">对象</param>
        /// <returns></returns>
        public static bool ObjToXml(string path, System.Object obj)
        {
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
                    {
                        //XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
                        //namespaces.Add(string.Empty, string.Empty);
                        XmlSerializer xs = new XmlSerializer(obj.GetType());
                        xs.Serialize(sw, obj);
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                Debug.LogError($"ObjToXml error，Type is {obj.GetType()}；Error：{e}");
                return false;
            }
        }

        /// <summary>
        /// Xml 转 对象（编辑器模式）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="path">Xml地址</param>
        /// <returns></returns>
        public static T XmlToObjInEditor<T>(string path) where T : class
        {
            T t = default(T);
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    XmlSerializer xs = new XmlSerializer(typeof(T));
                    t = (T) xs.Deserialize(fs);
                }
            }
            catch (Exception e)
            {
                Debug.LogError($"XmlToObjInEditor error，Type is {typeof(T)}；Error：{e}");
            }

            return t;
        }

        /// <summary>
        /// Xml 转 对象（编辑器模式）
        /// </summary>
        /// <param name="path">Xml地址</param>
        /// <param name="t">对象类型</param>
        /// <returns></returns>
        public static System.Object XmlToObjInEditor(string path, Type t)
        {
            System.Object obj = null;
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    XmlSerializer xs = new XmlSerializer(t);
                    obj = xs.Deserialize(fs);
                }
            }
            catch (Exception e)
            {
                Debug.LogError($"XmlToObjInEditor error，Type is {t.GetType()}；Error：{e}");
            }

            return obj;
        }

        /// <summary>
        /// 对象 转 二进制
        /// </summary>
        /// <param name="path">二进制存储地址</param>
        /// <param name="obj">对象</param>
        /// <returns></returns>
        public static bool ObjToBinary(string path, System.Object obj)
        {
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(fs, obj);
                }

                return true;
            }
            catch (Exception e)
            {
                Debug.LogError($"ObjToBinary error，Type is {obj.GetType()}；Error：{e}");
                return false;
            }
        }

        /// <summary>
        /// 二进制 转 对象（编辑器模式）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="path">二进制地址</param>
        /// <returns></returns>
        public static T BinaryToObjInEditor<T>(string path) where T : class
        {
            T t = default(T);
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    t = (T) bf.Deserialize(fs);
                }
            }
            catch (Exception e)
            {
                Debug.LogError($"BinaryToObjInEditor error，Type is {typeof(T)}；Error：{e}");
            }

            return t;
        }

        /// <summary>
        /// 二进制 转 对象（编辑器模式）
        /// </summary>
        /// <param name="path">二进制地址</param>
        /// <returns></returns>
        public static object BinaryToObjInEditor(string path)
        {
            object t = null;
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    t = bf.Deserialize(fs);
                }
            }
            catch (Exception e)
            {
                Debug.LogError($"BinaryToObjInEditor error，Path is {path}；Error：{e}");
            }

            return t;
        }

        #endregion


        //------------------------------  File 相关  ------------------------------//

        #region File 相关

        /// <summary>
        /// 检查指定文件夹是否存在，不存在则创建
        /// </summary>
        /// <param name="folderPath"></param>
        public static void CheckOrCreateFolder(string folderPath)
        {
            if (Directory.Exists(folderPath) == false)
            {
                Directory.CreateDirectory(folderPath);
            }
        }

        /// <summary>
        /// 将字符串写入指定路径的文件中
        /// </summary>
        /// <param name="content">待写入内容</param>
        /// <param name="filePath">文件路径</param>
        /// <param name="encoding">编码格式</param>
        public static void WriteStringToFile(string content, string filePath, Encoding encoding)
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                using (TextWriter txtWriter = new StreamWriter(fs, encoding))
                {
                    txtWriter.Write(content);
                }
            }
        }

        /// <summary>
        /// 获取文件名（不含后缀）
        /// 文件路径：xxx/xxxx/xx.xxx
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <param name="suffix">后缀（.xxx）</param>
        /// <returns></returns>
        public static string GetFileName(string path, string suffix)
        {
            if (string.IsNullOrEmpty(path)) return null;
            path = path.Replace("\\", "/");
            string[] table = path.Split('/');
            if (suffix == String.Empty)
                return table[table.Length - 1];
            else
                return table[table.Length - 1].Replace(suffix, string.Empty);
        }

        /// <summary>
        /// 搜索指定文件夹下所有的文件（DFS）
        /// </summary>
        public static List<string> GetFilesInFolder(string folderPath)
        {
            List<string> ret = new List<string>();
            FindFilesByDfs(folderPath, ref ret);
            return ret;
        }

        /// <summary>
        /// 通过文件后缀名来过滤文件路径集合
        /// </summary>
        public static List<string> FilterFileListBySuffix(IEnumerable<string> fileList, string suffix)
        {
            List<string> ret = new List<string>();
            foreach (var item in fileList)
            {
                if (item.EndsWith(suffix))
                {
                    ret.Add(item);
                }
            }

            return ret;
        }

        #endregion


        //------------------------------  正则相关  ------------------------------//

        #region 正则相关

        /// <summary>
        /// 验证字符串为数字（使用正则表达式）
        /// </summary>
        public static bool IsNumeric(string value)
        {
            return Regex.IsMatch(value, @"^[-+]?\d+(\.\d+)?$");
        }

        /// <summary>
        /// 判断是否为文件夹
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public static bool IsFolder(string path)
        {
            return !Regex.IsMatch(path, @"\.[\w]+$");
        }

        #endregion


        //------------------------------  字符串相关  ------------------------------//

        #region 字符串相关

        #endregion


        //------------------------------  Private Function  ------------------------------//

        #region Private Function

        /// <summary>
        /// 使用 DFS 搜索指定文件夹下所有子文件
        /// </summary>
        /// <param name="rootPath">文件夹路径</param>
        /// <param name="pathList">返回的子文件路径集合</param>
        static void FindFilesByDfs(string rootPath, ref List<string> pathList)
        {
            DirectoryInfo info = new DirectoryInfo(rootPath);
            foreach (var subFiles in info.GetFiles())
            {
                string suffix = Path.GetExtension(subFiles.FullName);
                pathList.Add(subFiles.FullName);
            }

            foreach (var subDire in info.GetDirectories())
            {
                string subPath = subDire.FullName;
                FindFilesByDfs(subPath, ref pathList);
            }
        }

        #endregion
    }
}